<!DOCTYPE html>
<html>
	<head>
		<title>WebSocket example</title>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link rel="stylesheet" href="https://unpkg.com/purecss@2.0.6/build/pure-min.css" integrity="sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5" crossorigin="anonymous">
		<style type="text/css">
			h1, h3 {
				text-align: center;
			}

			pre.sent, pre.received {
				margin-top: 0px;
				border-radius: 4px;
				padding: 5px;
			}

			pre.sent {
				border: 1px solid #4b63cc;
				background-color: white;
			}

			pre.received {
				border: 1px solid #4db96d;
				text-align: right;
			}

			.ws-state {
				font-weight: bold;
				line-height: 18px;
				vertical-align: middle;
			}

			div.log {
				font-size: 13pt;
			}
		</style>
	</head>

	<body>
		<div class="pure-g">
		<h1 class="pure-u-1">Webdis with HTML5 WebSockets</h1>
		</div>

		<div class="pure-g">
			<div class="pure-u-1-8"></div>
			<div class="pure-u-1-3" id="json-container"></div>

			<!-- spacer -->
			<div class="pure-u-1-12"></div>

			<div class="pure-u-1-3" id="raw-container"></div>
			<div class="pure-u-1-8"></div>
		</div>

<script type="text/javascript">
</script>

<script type="text/javascript">

$ = function(id) {return document.getElementById(id);};
const host = "127.0.0.1";
const port = 7379;

function installBlock(title, type) {
	const contents = `
	<h3>$TITLE</h3>
	<form class="pure-form">
		<fieldset>
			<div class="pure-g">
				<div class="pure-u-2-3"><label class="ws-state pure-u-23-24" id="$type-state">State: Disconnected</label></div>
				<div class="pure-u-1-3"><button type="submit" class="pure-u-23-24 pure-button pure-button-primary" id="$type-btn-connect">Connect</button></div>
			</div>
		</fieldset>
	</form>
	<form class="pure-form">
		<fieldset>
			<div class="pure-g">
				<div class="pure-u-2-3">&nbsp;</div>
				<div class="pure-u-1-3"><button disabled type="submit" class="pure-u-23-24 pure-button pure-button-primary" id="$type-btn-ping">Ping</button></div>
			</div>
		</fieldset>
	</form>
	<form class="pure-form">
		<fieldset>
			<div class="pure-g">
				<div class="pure-u-1-3"><input disabled class="pure-u-23-24" type="text" placeholder="key" id="$type-set-key" value="hello" /></div>
				<div class="pure-u-1-3"><input disabled class="pure-u-23-24" type="text" placeholder="value" id="$type-set-value" value="world" /></div>
				<div class="pure-u-1-3"><button disabled type="submit" class="pure-u-23-24 pure-button pure-button-primary" id="$type-btn-set">SET</button></div>
			</div>
		</fieldset>
	</form>
	<form class="pure-form">
		<fieldset>
			<div class="pure-g">
				<div class="pure-u-1-3">&nbsp;</div>
				<div class="pure-u-1-3"><input disabled class="pure-u-23-24" type="text" placeholder="key" id="$type-get-key" value="hello" /></div>
				<div class="pure-u-1-3"><button disabled type="submit" class="pure-u-23-24 pure-button pure-button-primary" id="$type-btn-get">GET</button></div>
			</div>
		</fieldset>
	</form>

	<form class="pure-form">
		<fieldset>
			<div class="pure-g">
				<div class="pure-u-1-3"><input disabled class="pure-u-23-24" type="text" placeholder="channel name" id="$type-pub-channel" value="channel-0" /></div>
				<div class="pure-u-1-3"><input disabled class="pure-u-23-24" type="text" placeholder="message value" id="$type-pub-message" value="message-0" /></div>
				<div class="pure-u-1-3"><button disabled type="submit" class="pure-u-23-24 pure-button pure-button-primary" id="$type-btn-pub">PUBLISH</button></div>
			</div>
		</fieldset>
	</form>

	<form class="pure-form">
		<fieldset>
			<div class="pure-g">
				<div class="pure-u-1-3">&nbsp;</div>
				<div class="pure-u-1-3"><input disabled class="pure-u-23-24" type="text" placeholder="channel" id="$type-sub-channel" value="channel-0" /></div>
				<div class="pure-u-1-3"><button disabled type="submit" class="pure-u-23-24 pure-button pure-button-primary" id="$type-btn-sub">SUBSCRIBE</button></div>
			</div>
		</fieldset>
	</form>

	<form class="pure-form">
		<fieldset>
			<div class="pure-g">
				<div class="pure-u-2-3">&nbsp;</div>
				<div class="pure-u-1-3"><button disabled type="submit" class="pure-u-23-24 pure-button pure-button-primary" id="$type-btn-debug">DEBUG OBJECT foo</button></div>
			</div>
		</fieldset>
	</form>

	<div class="pure-g">
		<div class="pure-u-2-3">&nbsp;</div>
		<div class="pure-u-1-3"><button disabled type="submit" class="pure-u-23-24 pure-button pure-button-primary" id="$type-btn-clear">Clear logs</button></div>
		<div class="log pure-u-1-1" id="$type-log">
		</div>
	</div>
	`;
	$(`${type}-container`).innerHTML = contents.replace(/\$TITLE/g, title).replace(/\$type/g, type);
}


class Client {
	constructor(type, serializer) {
		this.type = type;
		this.serializer = serializer;
		this.ws = null;
		this.connected = false;
		this.subscribed = false;
		this.logCount = 0;

		$(`${this.type}-btn-connect`).addEventListener('click', event => {
			event.preventDefault();
			console.log('Connecting...');
			this.ws = new WebSocket(`ws://${host}:${port}/.${this.type}`);
			window.ws = this.ws;
			this.ws.onopen = event => {
				console.log('Connected');
				this.setConnectedState(true);
			};

			// log received messages
			this.ws.onmessage = messageEvent => {
				this.log("received", messageEvent.data);
			};

			this.ws.onclose = event => {
				$(`${this.type}-btn-connect`).innerText = 'Connect';
				this.setConnectedState(false);
				this.subscribed = false;
			};
		});

		$(`${this.type}-btn-ping`).addEventListener('click', event => {
			event.preventDefault();
			const serialized = this.serializer(['PING']);
			this.send(serialized);
		});

		$(`${this.type}-btn-set`).addEventListener('click', event => {
			event.preventDefault();
			const serialized = this.serializer(['SET', $(`${this.type}-set-key`).value, $(`${this.type}-set-value`).value]);
			this.send(serialized);
		});

		$(`${this.type}-btn-get`).addEventListener('click', event => {
			event.preventDefault();
			const serialized = this.serializer(['GET', $(`${this.type}-get-key`).value]);
			this.send(serialized);
		});

		$(`${this.type}-btn-pub`).addEventListener('click', event => {
			event.preventDefault();
			const serialized = this.serializer(['PUBLISH', $(`${this.type}-pub-channel`).value, $(`${this.type}-pub-message`).value]);
			this.send(serialized);
		});

		$(`${this.type}-btn-sub`).addEventListener('click', event => {
			event.preventDefault();
			const serialized = this.serializer(['SUBSCRIBE', $(`${this.type}-sub-channel`).value]);
			try {
				this.send(serialized);
				this.subscribed = true;
				this.setConnectedState(true);
			} catch (e) {
				console.log('Error sending: ', serialized, e);
			}
		});

		$(`${this.type}-btn-debug`).addEventListener('click', event => {
			event.preventDefault();
			const serialized = this.serializer(['DEBUG', 'OBJECT', 'foo']);
			this.send(serialized);
		});

		$(`${this.type}-btn-clear`).addEventListener('click', event => {
			event.preventDefault();
			$(`${this.type}-log`).innerText = "";
			this.logCount = 0;
			this.updateLogButton();
		});
	}

	send(serialized) {
		this.log("sent", serialized);
		this.ws.send(serialized);
	}

	setConnectedState(connected) {
		this.connected = connected;
		$(`${this.type}-btn-connect`).disabled = connected;
		$(`${this.type}-btn-ping`).disabled = !connected || this.subscribed;
		$(`${this.type}-set-key`).disabled = !connected || this.subscribed;
		$(`${this.type}-set-value`).disabled = !connected || this.subscribed;
		$(`${this.type}-btn-set`).disabled = !connected || this.subscribed;
		$(`${this.type}-get-key`).disabled = !connected || this.subscribed;
		$(`${this.type}-btn-get`).disabled = !connected || this.subscribed;
		$(`${this.type}-pub-channel`).disabled = !connected || this.subscribed;
		$(`${this.type}-pub-message`).disabled = !connected || this.subscribed;
		$(`${this.type}-btn-pub`).disabled = !connected || this.subscribed;
		$(`${this.type}-sub-channel`).disabled = !connected || this.subscribed;
		$(`${this.type}-btn-sub`).disabled = !connected || this.subscribed;
		$(`${this.type}-btn-debug`).disabled = !connected || this.subscribed;

		$(`${this.type}-state`).innerText = `State: ${connected ? 'Connected' : 'Disconnected'}`;
	}

	updateLogButton() {
		$(`${this.type}-btn-clear`).disabled = this.logCount === 0;
	}

	log(dir, msg) {
		const id = `${this.type}-log`;

		const description = document.createElement("div");
		description.innerHTML = dir;
		$(id).appendChild(description);

		const contents = document.createElement("pre");
		contents.setAttribute("class", dir);
		contents.innerHTML = msg;
		$(id).appendChild(contents);
		this.logCount++;
		this.updateLogButton();
	}
}

function serializeRaw(args) {
	let raw = `*${args.length}\r\n`;
	for (let i = 0; i < args.length; i++) {
		raw += `$${args[i].length}\r\n`;
		raw += `${args[i]}\r\n`;
	}
	return raw;
}

addEventListener("DOMContentLoaded", () => {
	installBlock('JSON', 'json');
	installBlock('Raw', 'raw');

	const jsonClient = new Client('json', JSON.stringify);
	const rawClient = new Client('raw', serializeRaw);
});

		</script>
	</body>
</html>
